package com.maychat.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.maychat.enums.*;
import com.maychat.exception.BusinessException;
import com.maychat.mapper.*;
import com.maychat.pojo.*;
import com.maychat.pojo.extended.ChatSessionComb;
import com.maychat.pojo.extended.Message;
import com.maychat.pojo.extended.UserContactApplyWithGroupNameAndNickName;
import com.maychat.service.GroupInfoService;
import com.maychat.service.UserContactService;
import com.maychat.utils.StringTools;
import com.maychat.websocket.ChannelContextUtils;
import com.maychat.websocket.MessageHandler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

@Service
public class UserContactServiceImpl implements UserContactService {

    @Autowired
    private UserContactMapper userContactMapper;
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private GroupInfoMapper groupInfoMapper;
    @Autowired
    private UserContactApplyMapper userContactApplyMapper;
    @Autowired
    private RedisServiceImpl redisService;
    @Autowired
    private ChatSessionUserMapper chatSessionUserMapper;
    @Autowired
    private ChatMessageMapper chatMessageMapper;
    @Autowired
    private MessageHandler messageHandler;
    @Autowired
    private ChannelContextUtils channelContextUtils;
    @Lazy
    @Autowired
    private GroupInfoService groupInfoService;

    @Override
    public List<UserContact> selectUserContacts(UserContact userContact) {
        return userContactMapper.selectUserContacts(userContact);
    }

    @Override
    public Map<String, Object> searchById(String userId, String targetId) {
        ContactType type = ContactType.getByPrefix(targetId);
        Object result = null;
        if (type == null) return null;
        switch (type) {
            case USER -> {
                UserInfo query = new UserInfo();
                query.setUserId(targetId);
                List<UserInfo> res = userInfoMapper.selectUserInfos(query);
                if (res.isEmpty()) return null;
                result = res.get(0);
            }
            case GROUP -> {
                GroupInfo query = new GroupInfo();
                query.setGroupId(targetId);
                List<GroupInfo> res = groupInfoMapper.selectGroupInfos(query);
                if (res.isEmpty()) return null;
                query = res.get(0);
                Integer memberCount = groupInfoService.selectGroupMemberCount(targetId);
                query.setMemberCount(memberCount);
                result = query;
            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("result", result);
        map.put("contactType", type);
        UserContact query = new UserContact();
        query.setUserId(userId);
        query.setContactId(targetId);
        List<UserContact> res = userContactMapper.selectUserContacts(query);
        map.put("userContactStatus", res.isEmpty() ? UserContactStatus.NOT_FRIEND.ordinal() : res.get(0).getStatus());
        return map;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public JoinType addApply(UserInfoToken tokenObj, String targetId, String applyInfo) throws BusinessException {
        ContactType type = ContactType.getByPrefix(targetId);
        if (type == null) throw new BusinessException("错误ID");
        String applierId = tokenObj.getUserId();
        String receiveId = targetId;
        UserContact query = new UserContact();
        query.setUserId(applierId);
        query.setContactId(targetId);
        List<UserContact> res = userContactMapper.selectUserContacts(query);
        if (!res.isEmpty()) {
            UserContactStatus status = UserContactStatus.getByOrdinal(res.get(0).getStatus());
            switch (status) {
                case FRIEND -> throw new BusinessException("已经是好友了");
                case BAN, BANNED -> throw new BusinessException("已被拉黑，或你拉黑了对方");
            }
        }
        JoinType joinType = null;
        switch (type) {
            case USER -> {
                UserInfo query2 = new UserInfo();
                query2.setUserId(targetId);
                List<UserInfo> res2 = userInfoMapper.selectUserInfos(query2);
                if (res2.isEmpty())
                    throw new BusinessException("期望添加的用户不存在");
                joinType = JoinType.getByOrdinal(res2.get(0).getJoinType());
            }
            case GROUP -> {
                GroupInfo query2 = new GroupInfo();
                query2.setGroupId(targetId);
                List<GroupInfo> res2 = groupInfoMapper.selectGroupInfos(query2);
                if (res2.isEmpty() || (query2 = res2.get(0)).getStatus() == UserStatus.DISABLED.ordinal())
                    throw new BusinessException("期望群组不存在，或已解散");
                receiveId = query2.getGroupOwnerId();
                joinType = JoinType.getByOrdinal(query2.getJoinType());
            }
        }
        switch (joinType) {
            case DIRECT -> this.addContact(applierId, receiveId, targetId, type.ordinal(), applyInfo);
            case CHECK_FIRST -> {
                UserContactApply query2 = new UserContactApply();
                query2.setApplyUserId(applierId);
                query2.setReceiveUserId(receiveId);
                query2.setContactId(targetId);
                List<UserContactApply> res2 = userContactApplyMapper.selectUserContactApplies(query2);
                if (!res2.isEmpty()) {
                    UserContactApply apply = res2.get(0);
                    apply.setLastApplyTime(System.currentTimeMillis());
                    apply.setStatus(UserContactApplyStatus.PENDING.ordinal());
                    if (!StringUtils.isBlank(applyInfo))
                        apply.setApplyInfo(applyInfo);
                    userContactApplyMapper.updateUserContactApplyById(apply);
                } else {
                    query2.setStatus(UserContactApplyStatus.PENDING.ordinal());
                    query2.setLastApplyTime(System.currentTimeMillis());
                    query2.setContactType(type.ordinal());
                    query2.setApplyInfo(StringUtils.isBlank(applyInfo) ?
                            "我是" + tokenObj.getNickName() : applyInfo);
                    userContactApplyMapper.insertUserContactApply(query2);
                }
                if (res2.isEmpty() || res2.get(0).getStatus() != UserContactApplyStatus.PENDING.ordinal()) {
                    Message<Object> msg = new Message<>();
                    msg.setMessageType(MessageType.CONTACT_APPLY.ordinal());
                    msg.setMessageContent(applyInfo);
                    msg.setContactId(receiveId);
                    messageHandler.sendMsg(msg);
                }
            }
        }
        return joinType;
    }

    @Override
    public RowsWithTotal<UserContactApplyWithGroupNameAndNickName> selectUserContactPageByUserId(String userId, Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        UserContactApply query = new UserContactApply();
        query.setReceiveUserId(userId);
        Page<UserContactApplyWithGroupNameAndNickName> p = (Page<UserContactApplyWithGroupNameAndNickName>)
                userContactApplyMapper.selectUserContactAppliesByReceiverIdWithApplierAndGroup(userId);
        return new RowsWithTotal<>(p.getTotal(), p.getResult());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealApply(String userId, Integer applyId, Integer toStatus) throws BusinessException {
        UserContactApplyStatus status = UserContactApplyStatus.getByOrdinal(toStatus);
        if (status == null || status == UserContactApplyStatus.PENDING)
            throw new BusinessException("错误状态");
        UserContactApply query = new UserContactApply();
        query.setApplyId(applyId);
        List<UserContactApply> res = userContactApplyMapper.selectUserContactApplies(query);
        UserContactApply apply;
        if (res.isEmpty() || !Objects.equals((apply = res.get(0)).getReceiveUserId(), userId))
            throw new BusinessException("错误操作数据");
        UserContactApply update = new UserContactApply();
        update.setApplyId(applyId);
        update.setStatus(status.ordinal());
        update.setLastApplyTime(System.currentTimeMillis());
        userContactApplyMapper.updateUserContactApplyById(update);
        switch (status) {
            case FULFILLED -> this.addContact(apply.getApplyUserId(),
                    apply.getReceiveUserId(),
                    apply.getContactId(),
                    apply.getContactType(),
                    apply.getApplyInfo()
            );
            case BANNED -> {
                UserContact userContact = new UserContact();
                userContact.setUserId(apply.getApplyUserId());
                userContact.setContactId(apply.getContactId());
                userContact.setContactType(apply.getContactType());
                userContact.setCreateTime(LocalDateTime.now());
                userContact.setStatus(UserContactStatus.BANNED.ordinal());
                userContactMapper.upsert(userContact);
            }
        }
    }

    @Override
    public void addContact(String applyUserId, String receiveUserId, String contactId, Integer contactType, String applyInfo) throws BusinessException {
        List<UserContact> tot = new ArrayList<>();
        if (contactType == ContactType.GROUP.ordinal()) {
            // 是群聊
            UserContact query = new UserContact();
            query.setContactId(contactId);
            query.setStatus(UserContactStatus.FRIEND.ordinal());
            Integer count = userContactMapper.selectUserContactCount(query);
            MayChatSystemOptions systemOptions = redisService.getSystemOptions();
            if (count > systemOptions.getMaxGroupMemberCount())
                throw new BusinessException("群聊人数已满");
        }
        UserContact applyTo = new UserContact(applyUserId, contactId, contactType, LocalDateTime.now(), UserContactStatus.FRIEND.ordinal());
        tot.add(applyTo);
        // 假如双方都是人，那么还要反过来加一遍
        if (contactType == ContactType.USER.ordinal()) {
            UserContact toApply = new UserContact(contactId, applyUserId, contactType, LocalDateTime.now(),
                    UserContactStatus.FRIEND.ordinal());
            tot.add(toApply);
        }
        userContactMapper.insertMore(tot);
        // 如果双方都是人，反过来，添加缓存
        if (contactType == ContactType.USER.ordinal())
            redisService.addUserContact(receiveUserId, applyUserId);
        redisService.addUserContact(applyUserId, contactId);
        // 创建会话，发送消息
        String sessionId;
        if (contactType == ContactType.USER.ordinal())
            sessionId = StringTools.generateSessionId(applyUserId, contactId);
        else sessionId = StringTools.generateGroupSessionId(contactId);
        List<ChatSessionUser> chatSessionUsers = new ArrayList<>();
        if (contactType == ContactType.USER.ordinal()) {
            // 创建会话
            ChatSession chatSession = new ChatSession();
            chatSession.setSessionId(sessionId);
            chatSession.setLastMessage(applyInfo);
            chatSession.setLastReceiveTime(System.currentTimeMillis());
            chatSessionUserMapper.upsertChatSession(chatSession);

            // 申请人的session
            UserInfo contactUser = userInfoMapper.selectUserInfoById(contactId);
            ChatSessionUser chatSessionUserApply = new ChatSessionUser(
                    applyUserId,
                    contactId,
                    sessionId,
                    contactUser.getNickName(),
                    ContactType.USER.ordinal());
            chatSessionUsers.add(chatSessionUserApply);
            // 接收人
            UserInfo applyUser = userInfoMapper.selectUserInfoById(applyUserId);
            ChatSessionUser chatSessionUserContact = new ChatSessionUser(
                    contactId,
                    applyUserId,
                    sessionId,
                    applyUser.getNickName(),
                    ContactType.USER.ordinal());
            chatSessionUsers.add(chatSessionUserContact);
            chatSessionUserMapper.upsertChatSessionUserMore(chatSessionUsers);
            // 记录消息
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setSessionId(sessionId);
            chatMessage.setMessageType(MessageType.ADD_FRIEND.ordinal());
            chatMessage.setContactType(ContactType.USER.ordinal());
            chatMessage.setMessageContent(applyInfo);
            chatMessage.setSendUserId(applyUserId);
            chatMessage.setSendUserNickName(applyUser.getNickName());
            chatMessage.setContactId(contactId);
            chatMessage.setStatus(MessageStatus.SENT.ordinal());
            chatMessage.setSendTime(System.currentTimeMillis());
            chatMessageMapper.insertChatMessage(chatMessage);
            Message<UserInfo> msg = new Message<>();
            BeanUtils.copyProperties(chatMessage, msg);
            // 发送消息给接受申请的人
            messageHandler.sendMsg(msg);
            // 发送给申请人
            msg.setMessageType(MessageType.ADD_FRIEND_SELF.ordinal());
            msg.setContactId(applyUserId);
            msg.setExtendedData(contactUser);
            messageHandler.sendMsg(msg);
        } else {
            ChatSessionComb chatSessionComb = new ChatSessionComb();
            chatSessionComb.setSessionId(sessionId);
            chatSessionComb.setUserId(applyUserId);
            chatSessionComb.setContactId(contactId);
            GroupInfo query = new GroupInfo();
            query.setGroupId(contactId);
            List<GroupInfo> res = groupInfoMapper.selectGroupInfos(query);
            if (res.isEmpty()) throw new BusinessException();
            query = res.get(0);
            chatSessionComb.setContactName(query.getGroupName());
            chatSessionComb.setContactType(ContactType.GROUP.ordinal());
            chatSessionUserMapper.upsertChatSessionUserMore(List.of(chatSessionComb));

            UserInfo applyUserInfo = userInfoMapper.selectUserInfoById(applyUserId);
            String sendStr = applyUserInfo.getNickName() + MessageType.ADD_GROUP.getInitMessage();

            // 增加session消息
            ChatSession chatSession = new ChatSession();
            chatSession.setSessionId(sessionId);
            chatSession.setLastMessage(sendStr);
            chatSession.setLastReceiveTime(System.currentTimeMillis());
            chatSessionUserMapper.upsertChatSession(chatSession);

            // 增加聊天消息
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setSessionId(sessionId);
            chatMessage.setMessageType(MessageType.ADD_GROUP.ordinal());
            chatMessage.setSendTime(System.currentTimeMillis());
            chatMessage.setMessageContent(sendStr);
            chatMessage.setContactId(contactId);
            chatMessage.setContactType(ContactType.GROUP.ordinal());
            chatMessage.setStatus(MessageStatus.SENT.ordinal());
            chatMessage.setSendUserId(applyUserId);
            chatMessage.setSendUserNickName(applyUserInfo.getNickName());
            channelContextUtils.addUser2Group(applyUserId, contactId);
            chatMessageMapper.insertChatMessage(chatMessage);
            Message<Object> msg = new Message<>();
            BeanUtils.copyProperties(chatMessage, msg);
            msg.setContactId(contactId);
            UserContact query2 = new UserContact();
            query2.setContactId(contactId);
            query2.setStatus(UserContactStatus.FRIEND.ordinal());
            Integer memCount = userContactMapper.selectUserContactCount(query2);
            msg.setMemberCount(memCount);
            msg.setContactName(query.getGroupName());
            messageHandler.sendMsg(msg);
        }
    }

    @Override
    public List<UserInfo> selectFriendUsers(String userId) {
        return userInfoMapper.selectFriendUserInfos(userId);
    }

    @Override
    public List<GroupInfo> selectFriendGroups(String userId) {
        return groupInfoMapper.selectFriendGroupInfos(userId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rmUserContact(String userId, String contactId, UserContactStatus userContactStatus) {
        UserContact query = new UserContact();
        query.setUserId(userId);
        query.setContactId(contactId);
        query.setStatus(userContactStatus.ordinal());
        userContactMapper.updateUserContactById(query);
        // 另一边
        query.setUserId(contactId);
        query.setContactId(userId);
        switch (userContactStatus) {
            case DELETE -> query.setStatus(UserContactStatus.DELETED.ordinal());
            case BAN -> query.setStatus(UserContactStatus.BANNED.ordinal());
        }
        userContactMapper.updateUserContactById(query);
        // 从列表缓存中删除
        redisService.removeUserContact(contactId, userId);
        // 另一边
        redisService.removeUserContact(userId, contactId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addRobot(String uid) {
        MayChatSystemOptions systemOptions = redisService.getSystemOptions();
        String robotUId = systemOptions.getRobotUId();
        String robotNickName = systemOptions.getRobotNickName();
        String welcomeMsg = systemOptions.getWelcomeMsg();
        UserContact userContact = new UserContact();
        userContact.setUserId(uid);
        userContact.setContactId(robotUId);
        userContact.setContactType(ContactType.USER.ordinal());
        userContact.setCreateTime(LocalDateTime.now());
        userContact.setStatus(UserContactStatus.FRIEND.ordinal());
        userContactMapper.insertUserContact(userContact);
        // session
        String sessionId = StringTools.generateSessionId(uid, robotUId);
        ChatSession chatSession = new ChatSession();
        chatSession.setSessionId(sessionId);
        chatSession.setLastReceiveTime(System.currentTimeMillis());
        chatSession.setLastMessage(welcomeMsg);
        chatSessionUserMapper.insertChatSession(chatSession);
        // session_user
        ChatSessionUser chatSessionUser = new ChatSessionUser();
        chatSessionUser.setUserId(uid);
        chatSessionUser.setContactId(robotUId);
        chatSessionUser.setContactName(robotNickName);
        chatSessionUser.setSessionId(sessionId);
        chatSessionUserMapper.insertChatSessionUser(chatSessionUser);
        // 聊天消息
        ChatMessage chatMessage = new ChatMessage();
        chatMessage.setSessionId(sessionId);
        chatMessage.setMessageType(MessageType.CHAT.ordinal());
        chatMessage.setMessageContent(welcomeMsg);
        chatMessage.setSendUserId(robotUId);
        chatMessage.setSendUserNickName(robotNickName);
        chatMessage.setSendTime(System.currentTimeMillis());
        chatMessage.setContactId(uid);
        chatMessage.setContactType(ContactType.USER.ordinal());
        chatMessage.setStatus(MessageStatus.SENT.ordinal());
        chatMessageMapper.insertChatMessage(chatMessage);
    }


}
